Implement Sha256 bindings
authorAlex Crichton <alex@alexcrichton.com>
Mon, 8 Sep 2014 23:01:38 +0000 (16:01 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 16 Sep 2014 19:05:21 +0000 (12:05 -0700)
These are used to verify the downloads of packages from the registry.

src/cargo/lib.rs
src/cargo/sources/registry.rs
src/cargo/util/mod.rs
src/cargo/util/sha256.rs [new file with mode: 0644]

index 8ff267fcb38642a1a7a006d80f812f9dcbbe2cf0..99a15bb0b4292d8cf98a22106d89bcd5ce272207 100644 (file)
@@ -5,7 +5,7 @@
 #![feature(default_type_params)]
 #![deny(warnings)]
 
-extern crate glob;
+extern crate libc;
 extern crate regex;
 extern crate serialize;
 extern crate term;
@@ -17,6 +17,7 @@ extern crate curl;
 extern crate docopt;
 extern crate flate2;
 extern crate git2;
+extern crate glob;
 extern crate semver;
 extern crate tar;
 extern crate toml;
index edd096715ae063a3b3f7eb7b9b78366ba3c4446e..9579484ff34d09a399d1f4af8aa514b2f85b1446 100644 (file)
@@ -1,10 +1,13 @@
 #![allow(unused)]
 use std::io::{mod, fs, File, MemReader};
+use std::collections::HashMap;
+
 use curl::http;
 use git2;
 use semver::Version;
 use flate2::reader::GzDecoder;
 use serialize::json;
+use serialize::hex::ToHex;
 use tar::Archive;
 use url::Url;
 
@@ -12,7 +15,7 @@ use core::{Source, SourceId, PackageId, Package, Summary, Registry};
 use core::Dependency;
 use sources::PathSource;
 use util::{CargoResult, Config, internal, ChainError, ToUrl, human};
-use util::{hex, Require};
+use util::{hex, Require, Sha256};
 use ops;
 
 static CENTRAL: &'static str = "https://example.com";
@@ -25,6 +28,7 @@ pub struct RegistrySource<'a, 'b:'a> {
     config: &'a mut Config<'b>,
     handle: http::Handle,
     sources: Vec<PathSource>,
+    hashes: HashMap<(String, String), String>, // (name, vers) => cksum
 }
 
 #[deriving(Decodable)]
@@ -32,6 +36,14 @@ struct RegistryConfig {
     dl_url: String,
 }
 
+#[deriving(Decodable)]
+struct RegistryPackage {
+    name: String,
+    vers: String,
+    deps: Vec<String>,
+    cksum: String,
+}
+
 impl<'a, 'b> RegistrySource<'a, 'b> {
     pub fn new(source_id: &SourceId,
                config: &'a mut Config<'b>) -> RegistrySource<'a, 'b> {
@@ -46,6 +58,7 @@ impl<'a, 'b> RegistrySource<'a, 'b> {
             source_id: source_id.clone(),
             handle: http::Handle::new(),
             sources: Vec::new(),
+            hashes: HashMap::new(),
         }
     }
 
@@ -116,6 +129,23 @@ impl<'a, 'b> RegistrySource<'a, 'b> {
             return Err(internal(format!("Failed to get 200 reponse from {}\n{}",
                                         url, resp)))
         }
+
+        // Verify what we just downloaded
+        let expected = self.hashes.find(&(pkg.get_name().to_string(),
+                                          pkg.get_version().to_string()));
+        let expected = try!(expected.require(|| {
+            internal(format!("no hash listed for {}", pkg))
+        }));
+        let actual = {
+            let mut state = Sha256::new();
+            state.update(resp.get_body());
+            state.final()
+        };
+        if actual.as_slice().to_hex() != *expected {
+            return Err(human(format!("Failed to verify the checksum of `{}`",
+                                     pkg)))
+        }
+
         try!(File::create(&dst).write(resp.get_body()));
         Ok(dst)
     }
@@ -150,8 +180,8 @@ impl<'a, 'b> RegistrySource<'a, 'b> {
 
 impl<'a, 'b> Registry for RegistrySource<'a, 'b> {
     fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
-        let path = &self.checkout_path;
         let mut chars = dep.get_name().chars();
+        let path = self.checkout_path.clone();
         let path = path.join(format!("{}{}", chars.next().unwrap_or('X'),
                                      chars.next().unwrap_or('X')));
         let path = path.join(format!("{}{}", chars.next().unwrap_or('X'),
@@ -165,10 +195,8 @@ impl<'a, 'b> Registry for RegistrySource<'a, 'b> {
         let ret: CargoResult<Vec<Summary>>;
         ret = contents.as_slice().lines().filter(|l| l.trim().len() > 0)
                       .map(|l| {
-            #[deriving(Decodable)]
-            struct Package { name: String, vers: String, deps: Vec<String> }
 
-            let pkg = try!(json::decode::<Package>(l));
+            let pkg = try!(json::decode::<RegistryPackage>(l));
             let pkgid = try!(PackageId::new(pkg.name.as_slice(),
                                             pkg.vers.as_slice(),
                                             &self.source_id));
@@ -181,6 +209,8 @@ impl<'a, 'b> Registry for RegistrySource<'a, 'b> {
                 Dependency::parse(name, Some(vers), &self.source_id)
             }).collect();
             let deps = try!(deps);
+            let RegistryPackage { name, vers, cksum, .. } = pkg;
+            self.hashes.insert((name, vers), cksum);
             Ok(Summary::new(&pkgid, deps.as_slice()))
         }).collect();
         let mut summaries = try!(ret.chain_error(|| {
index 33bc04269331d1040beed407cb94ee3c856a74f0..38614d307b6920ac7114d0ee24bf91fe2536292a 100644 (file)
@@ -12,6 +12,7 @@ pub use self::dependency_queue::Dependency;
 pub use self::graph::Graph;
 pub use self::to_url::ToUrl;
 pub use self::vcs::{GitRepo, HgRepo};
+pub use self::sha256::Sha256;
 
 pub mod graph;
 pub mod process_builder;
@@ -26,4 +27,5 @@ pub mod profile;
 mod pool;
 mod dependency_queue;
 mod to_url;
-pub mod vcs;
+mod vcs;
+mod sha256;
diff --git a/src/cargo/util/sha256.rs b/src/cargo/util/sha256.rs
new file mode 100644 (file)
index 0000000..030bbd3
--- /dev/null
@@ -0,0 +1,162 @@
+pub use self::imp::Sha256;
+
+// Someone upstream will link to OpenSSL, so we don't need to explicitly
+// link to it ourselves. Hence we pick up Sha256 digests from OpenSSL
+#[cfg(not(windows))]
+mod imp {
+    use libc;
+
+    #[repr(C)] struct EVP_MD_CTX;
+    #[repr(C)] struct EVP_MD;
+    #[repr(C)] struct ENGINE;
+
+    extern {
+        fn EVP_DigestInit_ex(ctx: *mut EVP_MD_CTX,
+                             kind: *const EVP_MD,
+                             imp: *mut ENGINE) -> libc::c_int;
+        fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX,
+                            d: *const libc::c_void,
+                            cnt: libc::size_t) -> libc::c_int;
+        fn EVP_DigestFinal_ex(ctx: *mut EVP_MD_CTX, md: *mut libc::c_uchar,
+                              s: *mut libc::c_uint) -> libc::c_int;
+        fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX;
+        fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX);
+        fn EVP_sha256() -> *const EVP_MD;
+    }
+
+    pub struct Sha256 { ctx: *mut EVP_MD_CTX }
+
+    impl Sha256 {
+        pub fn new() -> Sha256 {
+            unsafe {
+                let ctx = EVP_MD_CTX_create();
+                assert!(!ctx.is_null());
+                let ret = Sha256 { ctx: ctx };
+                let n = EVP_DigestInit_ex(ret.ctx, EVP_sha256(), 0 as *mut _);
+                assert_eq!(n, 1);
+                return ret;
+            }
+        }
+
+        pub fn update(&mut self, bytes: &[u8]) {
+            unsafe {
+                let n = EVP_DigestUpdate(self.ctx, bytes.as_ptr() as *const _,
+                                         bytes.len() as libc::size_t);
+                assert_eq!(n, 1);
+            }
+        }
+
+        pub fn final(&mut self) -> [u8, ..32] {
+            unsafe {
+                let mut ret = [0u8, ..32];
+                let mut out = 0;
+                let n = EVP_DigestFinal_ex(self.ctx, ret.as_mut_ptr(), &mut out);
+                assert_eq!(n, 1);
+                assert_eq!(out, 32);
+                return ret;
+            }
+        }
+    }
+
+    impl Drop for Sha256 {
+        fn drop(&mut self) {
+            unsafe { EVP_MD_CTX_destroy(self.ctx) }
+        }
+    }
+}
+
+// Leverage the crypto APIs that windows has built in.
+#[cfg(windows)]
+mod imp {
+    use std::os;
+
+    use libc;
+    use libc::{DWORD, BYTE, LPCSTR, BOOL};
+    use libc::types::os::arch::extra::{LONG_PTR};
+
+    type HCRYPTPROV = LONG_PTR;
+    type HCRYPTHASH = LONG_PTR;
+    type HCRYPTKEY = LONG_PTR;
+    type ALG_ID = libc::c_uint;
+
+    static PROV_RSA_AES: DWORD = 24;
+    static CRYPT_SILENT: DWORD = 64;
+    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
+    static CALG_SHA_256: ALG_ID = 0x800c;
+    static HP_HASHVAL: DWORD = 0x00000002;
+
+    #[allow(non_snake_case)]
+    extern "system" {
+        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
+                                pszContainer: LPCSTR,
+                                pszProvider: LPCSTR,
+                                dwProvType: DWORD,
+                                dwFlags: DWORD) -> BOOL;
+        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
+
+
+        fn CryptCreateHash(hProv: HCRYPTPROV, Algid: ALG_ID, hKey: HCRYPTKEY,
+                           dwFlag: DWORD, phHash: *mut HCRYPTHASH) -> BOOL;
+        fn CryptHashData(hHash: HCRYPTHASH, pbData: *mut BYTE, dwDataLen: DWORD,
+                         dwFlags: DWORD) -> BOOL;
+        fn CryptGetHashParam(hHash: HCRYPTHASH, dwParam: DWORD, pbData: *mut BYTE,
+                             pdwDataLen: *mut DWORD, dwFlags: DWORD) -> BOOL;
+
+        fn CryptDestroyHash(hHash: HCRYPTHASH) -> BOOL;
+    }
+
+    macro_rules! call( ($e:expr) => ({
+        if $e == 0 {
+            fail!("failed {}: {}", stringify!($e), os::last_os_error())
+        }
+    }) )
+
+    pub struct Sha256 {
+        hcryptprov: HCRYPTPROV,
+        hcrypthash: HCRYPTHASH,
+    }
+
+    impl Sha256 {
+        pub fn new() -> Sha256 {
+            let mut hcp = 0;
+            call!(unsafe {
+                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
+                                     PROV_RSA_AES,
+                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
+            });
+            let mut ret = Sha256 { hcryptprov: hcp, hcrypthash: 0 };
+            call!(unsafe {
+                CryptCreateHash(ret.hcryptprov, CALG_SHA_256,
+                                0, 0, &mut ret.hcrypthash)
+            });
+            return ret;
+        }
+
+        pub fn update(&mut self, bytes: &[u8]) {
+            call!(unsafe {
+                CryptHashData(self.hcrypthash, bytes.as_ptr() as *mut _,
+                              bytes.len() as DWORD, 0)
+            })
+        }
+
+        pub fn final(&mut self) -> [u8, ..32] {
+            let mut ret = [0u8, ..32];
+            let mut len = ret.len() as libc::DWORD;
+            call!(unsafe {
+                CryptGetHashParam(self.hcrypthash, HP_HASHVAL, ret.as_mut_ptr(),
+                                  &mut len, 0)
+            });
+            assert_eq!(len as uint, ret.len());
+            return ret;
+        }
+    }
+
+    impl Drop for Sha256 {
+        fn drop(&mut self) {
+            if self.hcrypthash != 0 {
+                call!(unsafe { CryptDestroyHash(self.hcrypthash) });
+            }
+            call!(unsafe { CryptReleaseContext(self.hcryptprov, 0) });
+        }
+    }
+}